package com.example.jvm.service;

import com.example.common.service.TestService;
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.stereotype.Service;
import sun.misc.Unsafe;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

/**
 * @author zty
 * @apiNote
 * @project_name demo
 * @user tain
 * @create_at 2023/6/21 17:38
 * @create_vio IntelliJ IDEA
 */
@Service
public class JVMTestService implements TestService {

    @Override
    public Object doTest(Object param) {
        createClass();
        return null;
    }

    /**
     * 堆溢出
     * VM args: -Xms20m -Xms20m
     * 不断创建对象并保持对象被引用， 那么将会堆溢出
     * {@link OutOfMemoryError} : Java heap space
     */
    private void creatObject(){
        List<Object> objects = new ArrayList<>();
        while (true){
            objects.add(new Object());
        }
    }

    /**
     *  栈溢出1
     * 无限递归导致：
     * 如果JVM不支持栈扩展：抛出 {@link StackOverflowError}
     * 否则： 抛出@{@link OutOfMemoryError}
     */
    private void recursion(){
        recursion();
    }

    /**
     *栈溢出2
     * 创建无数局部变量导致局部变量表溢出：
     * 如果JVM不支持栈扩展：抛出 {@link StackOverflowError}
     * 否则： 抛出@{@link OutOfMemoryError}
     */
    private void createLocalVar(){
        long var0,var1,var2,var3,var4,var5,var6,var7,var8,var9,
                var10,var11,var12,var13,var14,var15,var16,var17,var18,var19,
                var20,var21,var22,var23,var24,var25,var26,var27,var28,var29,
                var30,var31,var32,var33,var34,var35,var36,var37,var38,var39,
                var40,var41,var42,var43,var44,var45,var46,var47,var48,var49,
                var50,var51,var52,var53,var54,var55,var56,var57,var58,var59,
                var60,var61,var62,var63,var64,var65,var66,var67,var68,var69,
                var70,var71,var72,var73,var74,var75,var76,var77,var78,var79,
                var80,var81,var82,var83,var84,var85,var86,var87,var88,var89,
                var90,var91,var92,var93,var94,var95,var96,var97,var98,var99;
        var0 = var1 = var2 = var3 = var4 = var5 = var6 = var7 = var8 = var9
                = var10 = var11 = var12 = var13 = var14 = var15 = var16 = var17 = var18 = var19
                = var20 = var21 = var22 = var23 = var24 = var25 = var26 = var27 = var28 = var29
                = var30 = var31 = var32 = var33 = var34 = var35 = var36 = var37 = var38 = var39
                = var40 = var41 = var42 = var43 = var44 = var45 = var46 = var47 = var48 = var49
                = var50 = var51 = var52 = var53 = var54 = var55 = var56 = var57 = var58 = var59
                = var60 = var61 = var62 = var63 = var64 = var65 = var66 = var67 = var68 = var69
                = var70 = var71 = var72 = var73 = var74 = var75 = var76 = var77 = var78 = var79
                = var80 = var81 = var82 = var83 = var84 = var85 = var86 = var87 = var88 = var89
                = var90 = var91 = var92 = var93 = var94 = var95 = var96 = var97 = var98 = var99 = 0;
    }

    /**
     * 栈溢出3
     * 创建无数线程导致栈溢出：
     * 减少最大的堆和栈空间使得每个线程拥有的空间不会太大。这样才能解决线程过多导致的内存溢出。
     */
    private void createThread(){
        while (true){
            new Thread(() -> {
                while (true){
                }
            }).start();
        }
    }

    /**
     * 元空间（方法区）的类型信息区溢出
     * 不断创建新的类导致方法区溢出
     * 限制方法区大小(jdk1.6): -XX:PermSize=10M -XX:MaxPermSize=10M jdk6
     * 限制元空间大小(jdk1.7): -XX:MetaspaceSize=32M -XX:MaxMetaspaceSize=32M  限制元空间
     * jdk8 Springboot 10M直接OutOfMemoryError 启动不了.
     */
    private void createClass(){
        while (true){
            Enhancer enhancer = new Enhancer();
            enhancer.setSuperclass(Object.class);
            enhancer.setUseCache(false);
            enhancer.setCallback((MethodInterceptor) (o, method, objects, methodProxy) -> methodProxy.invokeSuper(o, objects));
            enhancer.create();
        }
    }

    /**
     * 运行时常量池溢出
     * 不断创建String并把它丢到常量池导致运行时常量池溢出。
     * 由于jdk1.8把字符串常量池放到了堆中，所以在jdk1.8中导致的是堆溢出。
     */
    private void createConstant(){
        Set<String> set = new HashSet<>();
        int i = 0;
        while (true){
            set.add(String.valueOf(i++).intern());
        }
    }

    /**
     * 直接内存溢出
     * VM -XX:MaxDirectMemorySize=10M
     * @throws IllegalAccessException
     */
    private void allocateDirective() throws IllegalAccessException {
        Field unsafeField = Unsafe.class.getDeclaredFields()[0];
        unsafeField.setAccessible(true);
        Unsafe unsafe = (Unsafe)unsafeField.get(null);
        while (true){
            unsafe.allocateMemory(1L);
        }
    }

}
